/*
 * This file is subject to the terms of the GFX License. If a copy of
 * the license was not distributed with this file, you can obtain one at:
 *
 *              http://ugfx.org/license.html
 */

/**
 * @file    src/gdisp/driver.h
 * @brief   GDISP Graphic Driver subsystem low level driver header.
 *
 * @addtogroup GDISP
 * @{
 */

#ifndef _GDISP_LLD_H
#define _GDISP_LLD_H

#if GFX_USE_GDISP // || defined(__DOXYGEN__)

/*===========================================================================*/
/* Error checks.                                                             */
/*===========================================================================*/

#if GDISP_TOTAL_CONTROLLERS > 1 && !defined(GDISP_DRIVER_VMT)
	#define HARDWARE_AUTODETECT		2
	#define HARDWARE_DEFAULT		HARDWARE_AUTODETECT
#else
	#define HARDWARE_AUTODETECT		2
	#define HARDWARE_DEFAULT		FALSE
#endif

/**
 * @name    GDISP hardware accelerated support
 * @{
 */
	/**
	 * @brief   The display hardware can benefit from being flushed.
	 * @details Can be set to TRUE, FALSE or HARDWARE_AUTODETECT
	 *
	 * @note	HARDWARE_AUTODETECT is only meaningful when GDISP_TOTAL_CONTROLLERS > 1
	 * @note	Some controllers ** require ** the application to flush
	 */
	#ifndef GDISP_HARDWARE_FLUSH
		#define GDISP_HARDWARE_FLUSH		HARDWARE_DEFAULT
	#endif

	/**
	 * @brief   Hardware streaming writing is supported.
	 * @details Can be set to TRUE, FALSE or HARDWARE_AUTODETECT
	 *
	 * @note	HARDWARE_AUTODETECT is only meaningful when GDISP_TOTAL_CONTROLLERS > 1
	 * @note	Either GDISP_HARDWARE_STREAM_WRITE or GDISP_HARDWARE_DRAWPIXEL must be provided by each driver
	 */
	#ifndef GDISP_HARDWARE_STREAM_WRITE
		#define GDISP_HARDWARE_STREAM_WRITE		HARDWARE_DEFAULT
	#endif

	/**
	 * @brief   Hardware streaming reading of the display surface is supported.
	 * @details Can be set to TRUE, FALSE or HARDWARE_AUTODETECT
	 *
	 * @note	HARDWARE_AUTODETECT is only meaningful when GDISP_TOTAL_CONTROLLERS > 1
	 *
	 */
	#ifndef GDISP_HARDWARE_STREAM_READ
		#define GDISP_HARDWARE_STREAM_READ		HARDWARE_DEFAULT
	#endif

	/**
	 * @brief   Hardware supports setting the cursor position within the stream window.
	 * @details Can be set to TRUE, FALSE or HARDWARE_AUTODETECT
	 *
	 * @note	HARDWARE_AUTODETECT is only meaningful when GDISP_TOTAL_CONTROLLERS > 1
	 * @note	This is used to optimise setting of individual pixels within a stream window.
	 * 			It should therefore not be implemented unless it is cheaper than just setting
	 * 			a new window.
	 */
	#ifndef GDISP_HARDWARE_STREAM_POS
		#define GDISP_HARDWARE_STREAM_POS		HARDWARE_DEFAULT
	#endif

	/**
	 * @brief   Hardware accelerated draw pixel.
	 * @details Can be set to TRUE, FALSE or HARDWARE_AUTODETECT
	 *
	 * @note	HARDWARE_AUTODETECT is only meaningful when GDISP_TOTAL_CONTROLLERS > 1
	 * @note	Either GDISP_HARDWARE_STREAM_WRITE or GDISP_HARDWARE_DRAWPIXEL must be provided by the driver
	 */
	#ifndef GDISP_HARDWARE_DRAWPIXEL
		#define GDISP_HARDWARE_DRAWPIXEL		HARDWARE_DEFAULT
	#endif

	/**
	 * @brief   Hardware accelerated screen clears.
	 * @details Can be set to TRUE, FALSE or HARDWARE_AUTODETECT
	 *
	 * @note	HARDWARE_AUTODETECT is only meaningful when GDISP_TOTAL_CONTROLLERS > 1
	 * @note	This clears the entire display surface regardless of the clipping area currently set
	 */
	#ifndef GDISP_HARDWARE_CLEARS
		#define GDISP_HARDWARE_CLEARS			HARDWARE_DEFAULT
	#endif

	/**
	 * @brief   Hardware accelerated rectangular fills.
	 * @details Can be set to TRUE, FALSE or HARDWARE_AUTODETECT
	 *
	 * @note	HARDWARE_AUTODETECT is only meaningful when GDISP_TOTAL_CONTROLLERS > 1
	 */
	#ifndef GDISP_HARDWARE_FILLS
		#define GDISP_HARDWARE_FILLS			HARDWARE_DEFAULT
	#endif

	/**
	 * @brief   Hardware accelerated fills from an image.
	 * @details Can be set to TRUE, FALSE or HARDWARE_AUTODETECT
	 *
	 * @note	HARDWARE_AUTODETECT is only meaningful when GDISP_TOTAL_CONTROLLERS > 1
	 */
	#ifndef GDISP_HARDWARE_BITFILLS
		#define GDISP_HARDWARE_BITFILLS			HARDWARE_DEFAULT
	#endif

	/**
	 * @brief   Hardware accelerated scrolling.
	 * @details Can be set to TRUE, FALSE or HARDWARE_AUTODETECT
	 *
	 * @note	HARDWARE_AUTODETECT is only meaningful when GDISP_TOTAL_CONTROLLERS > 1
	 */
	#ifndef GDISP_HARDWARE_SCROLL
		#define GDISP_HARDWARE_SCROLL			HARDWARE_DEFAULT
	#endif

	/**
	 * @brief   Reading back of pixel values.
	 * @details Can be set to TRUE, FALSE or HARDWARE_AUTODETECT
	 *
	 * @note	HARDWARE_AUTODETECT is only meaningful when GDISP_TOTAL_CONTROLLERS > 1
	 */
	#ifndef GDISP_HARDWARE_PIXELREAD
		#define GDISP_HARDWARE_PIXELREAD		HARDWARE_DEFAULT
	#endif

	/**
	 * @brief   The driver supports one or more control commands.
	 * @details Can be set to TRUE, FALSE or HARDWARE_AUTODETECT
	 *
	 * @note	HARDWARE_AUTODETECT is only meaningful when GDISP_TOTAL_CONTROLLERS > 1
	 */
	#ifndef GDISP_HARDWARE_CONTROL
		#define GDISP_HARDWARE_CONTROL			HARDWARE_DEFAULT
	#endif

	/**
	 * @brief   The driver supports a non-standard query.
	 * @details Can be set to TRUE, FALSE or HARDWARE_AUTODETECT
	 *
	 * @note	HARDWARE_AUTODETECT is only meaningful when GDISP_TOTAL_CONTROLLERS > 1
	 */
	#ifndef GDISP_HARDWARE_QUERY
		#define GDISP_HARDWARE_QUERY			HARDWARE_DEFAULT
	#endif

	/**
	 * @brief   The driver supports a clipping in hardware.
	 * @details Can be set to TRUE, FALSE or HARDWARE_AUTODETECT
	 *
	 * @note	HARDWARE_AUTODETECT is only meaningful when GDISP_TOTAL_CONTROLLERS > 1
	 * @note	If this is defined the driver must perform its own clipping on all calls to
	 * 			the driver and respond appropriately if a parameter is outside the display area.
	 * @note	If this is not defined then the software ensures that all calls to the
	 * 			driver do not exceed the display area (provided GDISP_NEED_CLIP or GDISP_NEED_VALIDATION
	 * 			has been set).
	 */
	#ifndef GDISP_HARDWARE_CLIP
		#define GDISP_HARDWARE_CLIP				HARDWARE_DEFAULT
	#endif
/** @} */

/*===========================================================================*/
/* External declarations.                                                    */
/*===========================================================================*/

struct GDisplay {
	// The public GDISP stuff - must be the first element
	GDISPControl				g;

	#if GDISP_TOTAL_CONTROLLERS > 1
		const struct GDISPVMT const *	vmt;		// The Virtual Method Table
	#endif

	void *						priv;				// A private area just for the drivers use.
	void *						board;				// A private area just for the board interfaces use.

	uint8_t						systemdisplay;
	uint8_t						controllerdisplay;
	uint16_t					flags;
		#define GDISP_FLG_INSTREAM		0x0001		// We are in a user based stream operation
		#define GDISP_FLG_SCRSTREAM		0x0002		// The stream area currently covers the whole screen
		#define GDISP_FLG_DRIVER		0x0004		// This flags and above are for use by the driver

	// Multithread Mutex
	#if GDISP_NEED_MULTITHREAD
		gfxMutex				mutex;
	#endif

	// Software clipping
	#if GDISP_HARDWARE_CLIP != TRUE && (GDISP_NEED_CLIP || GDISP_NEED_VALIDATION)
		coord_t					clipx0, clipy0;
		coord_t					clipx1, clipy1;		/* not inclusive */
	#endif

	// Driver call parameters
	struct {
		coord_t			x, y;
		coord_t			cx, cy;
		coord_t			x1, y1;
		coord_t			x2, y2;
		color_t			color;
		void			*ptr;
	} p;

	// In call working buffers

	#if GDISP_NEED_TEXT
		// Text rendering parameters
		struct {
			font_t		font;
			color_t		color;
			color_t		bgcolor;
			coord_t		clipx0, clipy0;
			coord_t		clipx1, clipy1;
		} t;
	#endif
	#if GDISP_LINEBUF_SIZE != 0 && ((GDISP_NEED_SCROLL && !GDISP_HARDWARE_SCROLL) || (!GDISP_HARDWARE_STREAM_WRITE && GDISP_HARDWARE_BITFILLS))
		// A pixel line buffer
		color_t		linebuf[GDISP_LINEBUF_SIZE];
	#endif

};

#if GDISP_TOTAL_CONTROLLERS == 1 || defined(GDISP_DRIVER_VMT) || defined(__DOXYGEN__)
	#if GDISP_TOTAL_CONTROLLERS > 1
		#define LLDSPEC			static
	#else
		#define LLDSPEC
	#endif

	#ifdef __cplusplus
	extern "C" {
	#endif

	/**
	 * @brief   Initialize the driver.
	 * @return	TRUE if successful.
	 * @param[in]	g		The driver structure
	 * @param[out]	g->g	The driver must fill in the GDISPControl structure
	 */
	LLDSPEC	bool_t gdisp_lld_init(GDisplay *g);

	#if GDISP_HARDWARE_FLUSH || defined(__DOXYGEN__)
		/**
		 * @brief   Flush the current drawing operations to the display
		 * @pre		GDISP_HARDWARE_FLUSH is TRUE
		 *
		 * @param[in]	g				The driver structure
		 *
		 * @note		The parameter variables must not be altered by the driver.
		 */
		LLDSPEC	void gdisp_lld_flush(GDisplay *g);
	#endif

	#if GDISP_HARDWARE_STREAM_WRITE || defined(__DOXYGEN__)
		/**
		 * @brief   Start a streamed write operation
		 * @pre		GDISP_HARDWARE_STREAM_WRITE is TRUE
		 *
		 * @param[in]	g				The driver structure
		 *
		 * @note		g->p.x,g->p.y	The window position
		 * @note		g->p.cx,g->p.cy	The window size
		 *
		 * @note		The parameter variables must not be altered by the driver.
		 * @note		Streaming operations that wrap the defined window have
		 * 				undefined results.
		 * @note		This must be followed by a call to @p gdisp_lld_write_pos() if GDISP_HARDWARE_STREAM_POS is TRUE.
		 */
		LLDSPEC	void gdisp_lld_write_start(GDisplay *g);

		/**
		 * @brief   Send a pixel to the current streaming position and then increment that position
		 * @pre		GDISP_HARDWARE_STREAM_WRITE is TRUE
		 *
		 * @param[in]	g				The driver structure
		 *
		 * @note		g->p.color		The color to display at the curent position
		 * @note		The parameter variables must not be altered by the driver.
		 */
		LLDSPEC	void gdisp_lld_write_color(GDisplay *g);

		/**
		 * @brief   End the current streaming write operation
		 * @pre		GDISP_HARDWARE_STREAM_WRITE is TRUE
		 *
		 * @param[in]	g				The driver structure
		 *
		 * @note		The parameter variables must not be altered by the driver.
		 */
		LLDSPEC	void gdisp_lld_write_stop(GDisplay *g);

		#if GDISP_HARDWARE_STREAM_POS || defined(__DOXYGEN__)
			/**
			 * @brief   Change the current position within the current streaming window
			 * @pre		GDISP_HARDWARE_STREAM_POS is TRUE and GDISP_HARDWARE_STREAM_WRITE is TRUE
			 *
			 * @param[in]	g				The driver structure
			 * @param[in]	g->p.x,g->p.y	The new position (which will always be within the existing stream window)
			 *
			 * @note		The parameter variables must not be altered by the driver.
			 */
			LLDSPEC	void gdisp_lld_write_pos(GDisplay *g);
		#endif
	#endif

	#if GDISP_HARDWARE_STREAM_READ || defined(__DOXYGEN__)
		/**
		 * @brief   Start a streamed read operation
		 * @pre		GDISP_HARDWARE_STREAM_READ is TRUE
		 *
		 * @param[in]	g				The driver structure
		 * @param[in]	g->p.x,g->p.y	The window position
		 * @param[in]	g->p.cx,g->p.cy	The window size
		 *
		 * @note		The parameter variables must not be altered by the driver.
		 * @note		Streaming operations that wrap the defined window have
		 * 				undefined results.
		 */
		LLDSPEC	void gdisp_lld_read_start(GDisplay *g);

		/**
		 * @brief   Read a pixel from the current streaming position and then increment that position
		 * @return	The color at the current position
		 * @pre		GDISP_HARDWARE_STREAM_READ is TRUE
		 *
		 * @param[in]	g				The driver structure
		 *
		 * @note		The parameter variables must not be altered by the driver.
		 */
		LLDSPEC	color_t gdisp_lld_read_color(GDisplay *g);

		/**
		 * @brief   End the current streaming operation
		 * @pre		GDISP_HARDWARE_STREAM_READ is TRUE
		 *
		 * @param[in]	g				The driver structure
		 *
		 * @note		The parameter variables must not be altered by the driver.
		 */
		LLDSPEC	void gdisp_lld_read_stop(GDisplay *g);
	#endif

	#if GDISP_HARDWARE_DRAWPIXEL || defined(__DOXYGEN__)
		/**
		 * @brief   Draw a pixel
		 * @pre		GDISP_HARDWARE_DRAWPIXEL is TRUE
		 *
		 * @param[in]	g				The driver structure
		 * @param[in]	g->p.x,g->p.y	The pixel position
		 * @param[in]	g->p.color		The color to set
		 *
		 * @note		The parameter variables must not be altered by the driver.
		 */
		LLDSPEC	void gdisp_lld_draw_pixel(GDisplay *g);
	#endif

	#if GDISP_HARDWARE_CLEARS || defined(__DOXYGEN__)
		/**
		 * @brief   Clear the screen using the defined color
		 * @pre		GDISP_HARDWARE_CLEARS is TRUE
		 *
		 * @param[in]	g				The driver structure
		 * @param[in]	g->p.color		The color to set
		 *
		 * @note		The parameter variables must not be altered by the driver.
		 */
		LLDSPEC	void gdisp_lld_clear(GDisplay *g);
	#endif

	#if GDISP_HARDWARE_FILLS || defined(__DOXYGEN__)
		/**
		 * @brief   Fill an area with a single color
		 * @pre		GDISP_HARDWARE_FILLS is TRUE
		 *
		 * @param[in]	g				The driver structure
		 * @param[in]	g->p.x,g->p.y	The area position
		 * @param[in]	g->p.cx,g->p.cy	The area size
		 * @param[in]	g->p.color		The color to set
		 *
		 * @note		The parameter variables must not be altered by the driver.
		 */
		LLDSPEC	void gdisp_lld_fill_area(GDisplay *g);
	#endif

	#if GDISP_HARDWARE_BITFILLS || defined(__DOXYGEN__)
		/**
		 * @brief   Fill an area using a bitmap
		 * @pre		GDISP_HARDWARE_BITFILLS is TRUE
		 *
		 * @param[in]	g				The driver structure
		 * @param[in]	g->p.x,g->p.y	The area position
		 * @param[in]	g->p.cx,g->p.cy	The area size
		 * @param[in]	g->p.x1,g->p.y1	The starting position in the bitmap
		 * @param[in]	g->p.x2			The width of a bitmap line
		 * @param[in]	g->p.ptr		The pointer to the bitmap
		 *
		 * @note		The parameter variables must not be altered by the driver.
		 */
		LLDSPEC	void gdisp_lld_blit_area(GDisplay *g);
	#endif

	#if GDISP_HARDWARE_PIXELREAD || defined(__DOXYGEN__)
		/**
		 * @brief   Read a pixel from the display
		 * @return	The color at the defined position
		 * @pre		GDISP_HARDWARE_PIXELREAD is TRUE (and the application needs it)
		 *
		 * @param[in]	g				The driver structure
		 * @param[in]	g->p.x,g->p.y	The pixel position
		 *
		 * @note		The parameter variables must not be altered by the driver.
		 */
		LLDSPEC	color_t gdisp_lld_get_pixel_color(GDisplay *g);
	#endif

	#if (GDISP_HARDWARE_SCROLL && GDISP_NEED_SCROLL) || defined(__DOXYGEN__)
		/**
		 * @brief   Scroll an area of the screen
		 * @pre		GDISP_HARDWARE_SCROLL is TRUE (and the application needs it)
		 *
		 * @param[in]	g				The driver structure
		 * @param[in]	g->p.x,g->p.y	The area position
		 * @param[in]	g->p.cx,g->p.cy	The area size
		 * @param[in]	g->p.y1			The number of lines to scroll (positive or negative)
		 *
		 * @note		The parameter variables must not be altered by the driver.
		 * @note		This can be easily implemented if the hardware supports
		 * 				display area to display area copying.
		 * @note		Clearing the exposed area on the scroll operation is not
		 * 				needed as the high level code handles this.
		 */
		LLDSPEC	void gdisp_lld_vertical_scroll(GDisplay *g);
	#endif

	#if (GDISP_HARDWARE_CONTROL && GDISP_NEED_CONTROL) || defined(__DOXYGEN__)
		/**
		 * @brief   Control some feature of the hardware
		 * @pre		GDISP_HARDWARE_CONTROL is TRUE (and the application needs it)
		 *
		 * @param[in]	g				The driver structure
		 * @param[in]	g->p.x			The operation to perform
		 * @param[in]	g->p.ptr		The operation parameter
		 *
		 * @note		The parameter variables must not be altered by the driver.
		 */
		LLDSPEC	void gdisp_lld_control(GDisplay *g);
	#endif

	#if (GDISP_HARDWARE_QUERY && GDISP_NEED_QUERY) || defined(__DOXYGEN__)
		/**
		 * @brief   Query some feature of the hardware
		 * @return	The information requested (typecast as void *)
		 * @pre		GDISP_HARDWARE_QUERY is TRUE (and the application needs it)
		 *
		 * @param[in]	g				The driver structure
		 * @param[in]	g->p.x			What to query
		 *
		 * @note		The parameter variables must not be altered by the driver.
		 */
		LLDSPEC	void *gdisp_lld_query(GDisplay *g);				// Uses p.x (=what);
	#endif

	#if (GDISP_HARDWARE_CLIP && (GDISP_NEED_CLIP || GDISP_NEED_VALIDATION)) || defined(__DOXYGEN__)
		/**
		 * @brief   Set the hardware clipping area
		 * @pre		GDISP_HARDWARE_CLIP is TRUE (and the application needs it)
		 *
		 * @param[in]	g				The driver structure
		 * @param[in]	g->p.x,g->p.y	The area position
		 * @param[in]	g->p.cx,g->p.cy	The area size
		 *
		 * @note		The parameter variables must not be altered by the driver.
		 */
		LLDSPEC	void gdisp_lld_set_clip(GDisplay *g);
	#endif

	#ifdef __cplusplus
	}
	#endif
#endif  // GDISP_TOTAL_CONTROLLERS == 1 || defined(GDISP_DRIVER_VMT)


#if GDISP_TOTAL_CONTROLLERS > 1

	typedef struct GDISPVMT {
		bool_t (*init)(GDisplay *g);
		void (*writestart)(GDisplay *g);				// Uses p.x,p.y  p.cx,p.cy
		void (*writepos)(GDisplay *g);					// Uses p.x,p.y
		void (*writecolor)(GDisplay *g);				// Uses p.color
		void (*writestop)(GDisplay *g);					// Uses no parameters
		void (*readstart)(GDisplay *g);					// Uses p.x,p.y  p.cx,p.cy
		color_t (*readcolor)(GDisplay *g);				// Uses no parameters
		void (*readstop)(GDisplay *g);					// Uses no parameters
		void (*pixel)(GDisplay *g);						// Uses p.x,p.y  p.color
		void (*clear)(GDisplay *g);						// Uses p.color
		void (*fill)(GDisplay *g);						// Uses p.x,p.y  p.cx,p.cy  p.color
		void (*blit)(GDisplay *g);						// Uses p.x,p.y  p.cx,p.cy  p.x1,p.y1 (=srcx,srcy)  p.x2 (=srccx), p.ptr (=buffer)
		color_t (*get)(GDisplay *g);					// Uses p.x,p.y
		void (*vscroll)(GDisplay *g);					// Uses p.x,p.y  p.cx,p.cy, p.y1 (=lines) p.color
		void (*control)(GDisplay *g);					// Uses p.x (=what)  p.ptr (=value)
		void *(*query)(GDisplay *g);					// Uses p.x (=what);
		void (*setclip)(GDisplay *g);					// Uses p.x,p.y  p.cx,p.cy
		void (*flush)(GDisplay *g);						// Uses no parameters
	} GDISPVMT;

	#if defined(GDISP_DRIVER_VMT)
		#if !GDISP_HARDWARE_STREAM_WRITE && !GDISP_HARDWARE_DRAWPIXEL
			#error "GDISP Driver: Either GDISP_HARDWARE_STREAM_WRITE or GDISP_HARDWARE_DRAWPIXEL must be TRUE"
		#endif
		const GDISPVMT const GDISP_DRIVER_VMT[1] = {{
			gdisp_lld_init,
			#if GDISP_HARDWARE_FLUSH
				gdisp_lld_flush,
			#else
				0,
			#endif
			#if GDISP_HARDWARE_STREAM_WRITE
				gdisp_lld_write_start,
				#if GDISP_HARDWARE_STREAM_POS
					gdisp_lld_write_pos,
				#else
					0,
				#endif
				gdisp_lld_write_color,
				gdisp_lld_write_stop,
			#else
				0, 0, 0, 0,
			#endif
			#if GDISP_HARDWARE_STREAM_READ
				gdisp_lld_read_start,
				gdisp_lld_read_color,
				gdisp_lld_read_stop,
			#else
				0, 0, 0,
			#endif
			#if GDISP_HARDWARE_DRAWPIXEL
				gdisp_lld_draw_pixel,
			#else
				0,
			#endif
			#if GDISP_HARDWARE_CLEARS
				gdisp_lld_clear,
			#else
				0,
			#endif
			#if GDISP_HARDWARE_FILLS
				gdisp_lld_fill_area,
			#else
				0,
			#endif
			#if GDISP_HARDWARE_BITFILLS
				gdisp_lld_blit_area,
			#else
				0,
			#endif
			#if GDISP_HARDWARE_PIXELREAD
				gdisp_lld_get_pixel_color,
			#else
				0,
			#endif
			#if GDISP_HARDWARE_SCROLL && GDISP_NEED_SCROLL
				gdisp_lld_vertical_scroll,
			#else
				0,
			#endif
			#if GDISP_HARDWARE_CONTROL && GDISP_NEED_CONTROL
				gdisp_lld_control,
			#else
				0,
			#endif
			#if GDISP_HARDWARE_QUERY && GDISP_NEED_QUERY
				gdisp_lld_query,
			#else
				0,
			#endif
			#if GDISP_HARDWARE_CLIP && (GDISP_NEED_CLIP || GDISP_NEED_VALIDATION)
				gdisp_lld_set_clip,
			#else
				0,
			#endif
		}};

	#else
		#define gdisp_lld_init(g)				g->vmt->init(g)
		#define gdisp_lld_flush(g)				g->vmt->flush(g)
		#define gdisp_lld_write_start(g)		g->vmt->writestart(g)
		#define gdisp_lld_write_pos(g)			g->vmt->writepos(g)
		#define gdisp_lld_write_color(g)		g->vmt->writecolor(g)
		#define gdisp_lld_write_stop(g)			g->vmt->writestop(g)
		#define gdisp_lld_read_start(g)			g->vmt->readstart(g)
		#define gdisp_lld_read_color(g)			g->vmt->readcolor(g)
		#define gdisp_lld_read_stop(g)			g->vmt->readstop(g)
		#define gdisp_lld_draw_pixel(g)			g->vmt->pixel(g)
		#define gdisp_lld_clear(g)				g->vmt->clear(g)
		#define gdisp_lld_fill_area(g)			g->vmt->fill(g)
		#define gdisp_lld_blit_area(g)			g->vmt->blit(g)
		#define gdisp_lld_get_pixel_color(g)	g->vmt->get(g)
		#define gdisp_lld_vertical_scroll(g)	g->vmt->vscroll(g)
		#define gdisp_lld_control(g)			g->vmt->control(g)
		#define gdisp_lld_query(g)				g->vmt->query(g)
		#define gdisp_lld_set_clip(g)			g->vmt->setclip(g)
	#endif	// GDISP_LLD_DECLARATIONS

#endif		// GDISP_TOTAL_CONTROLLERS > 1

/* Verify information for packed pixels and define a non-packed pixel macro */
#if !GDISP_PACKED_PIXELS
	#define gdispPackPixels(buf,cx,x,y,c)	{ ((color_t *)(buf))[(y)*(cx)+(x)] = (c); }
#elif !GDISP_HARDWARE_BITFILLS
	#error "GDISP: packed pixel formats are only supported for hardware accelerated drivers."
#elif GDISP_PIXELFORMAT != GDISP_PIXELFORMAT_RGB888 \
		&& GDISP_PIXELFORMAT != GDISP_PIXELFORMAT_RGB444 \
		&& GDISP_PIXELFORMAT != GDISP_PIXELFORMAT_RGB666 \
		&& GDISP_PIXELFORMAT != GDISP_PIXELFORMAT_CUSTOM
	#error "GDISP: A packed pixel format has been specified for an unsupported pixel format."
#endif

/* Support routine for packed pixel formats */
#if !defined(gdispPackPixels) || defined(__DOXYGEN__)
	/**
	 * @brief   Pack a pixel into a pixel buffer.
	 * @note    This function performs no buffer boundary checking
	 *			regardless of whether GDISP_NEED_CLIP has been specified.
	 *
	 * @param[in] buf		The buffer to put the pixel in
	 * @param[in] cx		The width of a pixel line
	 * @param[in] x, y		The location of the pixel to place
	 * @param[in] color		The color to put into the buffer
	 *
	 * @api
	 */
	void gdispPackPixels(const pixel_t *buf, coord_t cx, coord_t x, coord_t y, color_t color);
#endif

/* Low level driver pixel format information */
//-------------------------
//	True-Color color system
//-------------------------
#if GDISP_LLD_PIXELFORMAT & GDISP_COLORSYSTEM_TRUECOLOR
	#define LLDCOLOR_SYSTEM			GDISP_COLORSYSTEM_TRUECOLOR

	// Calculate the number of bits
	#define LLDCOLOR_BITS_R			((GDISP_LLD_PIXELFORMAT>>8) & 0x0F)
	#define LLDCOLOR_BITS_G			((GDISP_LLD_PIXELFORMAT>>4) & 0x0F)
	#define LLDCOLOR_BITS_B			((GDISP_LLD_PIXELFORMAT>>0) & 0x0F)
	#define LLDCOLOR_BITS			(LLDCOLOR_BITS_R + LLDCOLOR_BITS_G + LLDCOLOR_BITS_B)

	// From the number of bits determine COLOR_TYPE, COLOR_TYPE_BITS and masking
	#if LLDCOLOR_BITS <= 8
		#define LLDCOLOR_TYPE			uint8_t
		#define LLDCOLOR_TYPE_BITS		8
	#elif LLDCOLOR_BITS <= 16
		#define LLDCOLOR_TYPE			uint16_t
		#define LLDCOLOR_TYPE_BITS		16
	#elif LLDCOLOR_BITS <= 32
		#define LLDCOLOR_TYPE			uint32_t
		#define LLDCOLOR_TYPE_BITS		32
	#else
		#error "GDISP: Cannot define low level driver color types with more than 32 bits"
	#endif
	#if LLDCOLOR_TYPE_BITS == LLDCOLOR_BITS
		#define LLDCOLOR_NEEDS_MASK	FALSE
	#else
		#define LLDCOLOR_NEEDS_MASK	TRUE
	#endif
	#define LLDCOLOR_MASK()			((1 << LLDCOLOR_BITS)-1)

	// Calculate the component bit shifts
	#if (GDISP_LLD_PIXELFORMAT & GDISP_COLORSYSTEM_MASK) == GDISP_COLORSYSTEM_RGB
		#define LLDCOLOR_SHIFT_R		(LLDCOLOR_BITS_B+LLDCOLOR_BITS_G)
		#define LLDCOLOR_SHIFT_G		LLDCOLOR_BITS_B
		#define LLDCOLOR_SHIFT_B		0
	#else
		#define LLDCOLOR_SHIFT_B		(LLDCOLOR_BITS_R+LLDCOLOR_BITS_G)
		#define LLDCOLOR_SHIFT_G		LLDCOLOR_BITS_R
		#define LLDCOLOR_SHIFT_R		0
	#endif

	// Calculate LLDRED_OF, LLDGREEN_OF, LLDBLUE_OF and LLDRGB2COLOR
	#if LLDCOLOR_BITS_R + LLDCOLOR_SHIFT_R == 8
		#define LLDRED_OF(c)			((c) & (((1<<LLDCOLOR_BITS_R)-1) << LLDCOLOR_SHIFT_R))
		#define LLDRGB2COLOR_R(r)		((LLDCOLOR_TYPE)((r) & (0xFF & ~((1<<(8-LLDCOLOR_BITS_R))-1))))
	#elif LLDCOLOR_BITS_R + LLDCOLOR_SHIFT_R > 8
		#define LLDRED_OF(c)			(((c) & (((1<<LLDCOLOR_BITS_R)-1) << LLDCOLOR_SHIFT_R)) >> (LLDCOLOR_BITS_R+LLDCOLOR_SHIFT_R-8))
		#define LLDRGB2COLOR_R(r)		(((LLDCOLOR_TYPE)((r) & (0xFF & ~((1<<(8-LLDCOLOR_BITS_R))-1)))) << (LLDCOLOR_BITS_R+LLDCOLOR_SHIFT_R-8))
	#else // LLDCOLOR_BITS_R + LLDCOLOR_SHIFT_R < 8
		#define LLDRED_OF(c)			(((c) & (((1<<LLDCOLOR_BITS_R)-1) << LLDCOLOR_SHIFT_R)) << (8-(LLDCOLOR_BITS_R+LLDCOLOR_SHIFT_R)))
		#define LLDRGB2COLOR_R(r)		(((LLDCOLOR_TYPE)((r) & (0xFF & ~((1<<(8-LLDCOLOR_BITS_R))-1)))) >> (8-(LLDCOLOR_BITS_R+LLDCOLOR_SHIFT_R)))
	#endif
	#if LLDCOLOR_BITS_G + LLDCOLOR_SHIFT_G == 8
		#define LLDGREEN_OF(c)			((c) & (((1<<LLDCOLOR_BITS_G)-1) << LLDCOLOR_SHIFT_G))
		#define LLDRGB2COLOR_G(g)		((LLDCOLOR_TYPE)((g) & (0xFF & ~((1<<(8-LLDCOLOR_BITS_G))-1))))
	#elif LLDCOLOR_BITS_G + LLDCOLOR_SHIFT_G > 8
		#define LLDGREEN_OF(c)			(((c) & (((1<<LLDCOLOR_BITS_G)-1) << LLDCOLOR_SHIFT_G)) >> (LLDCOLOR_BITS_G+LLDCOLOR_SHIFT_G-8))
		#define LLDRGB2COLOR_G(g)		(((LLDCOLOR_TYPE)((g) & (0xFF & ~((1<<(8-LLDCOLOR_BITS_G))-1)))) << (LLDCOLOR_BITS_G+LLDCOLOR_SHIFT_G-8))
	#else // LLDCOLOR_BITS_G + LLDCOLOR_SHIFT_G < 8
		#define LLDGREEN_OF(c)			(((c) & (((1<<LLDCOLOR_BITS_G)-1) << LLDCOLOR_SHIFT_G)) << (8-(LLDCOLOR_BITS_G+LLDCOLOR_SHIFT_G)))
		#define LLDRGB2COLOR_G(g)		(((LLDCOLOR_TYPE)((g) & (0xFF & ~((1<<(8-LLDCOLOR_BITS_G))-1)))) >> (8-(LLDCOLOR_BITS_LLDG+COLOR_SHIFT_G)))
	#endif
	#if LLDCOLOR_BITS_B + LLDCOLOR_SHIFT_B == 8
		#define LLDBLUE_OF(c)			((c) & (((1<<LLDCOLOR_BITS_B)-1) << LLDCOLOR_SHIFT_B))
		#define LLDRGB2COLOR_B(b)		((LLDCOLOR_TYPE)((b) & (0xFF & ~((1<<(8-LLDCOLOR_BITS_B))-1))))
	#elif LLDCOLOR_BITS_B + LLDCOLOR_SHIFT_B > 8
		#define LLDBLUE_OF(c)			(((c) & (((1<<LLDCOLOR_BITS_B)-1) << LLDCOLOR_SHIFT_B)) >> (LLDCOLOR_BITS_B+LLDCOLOR_SHIFT_B-8))
		#define LLDRGB2COLOR_B(b)		(((LLDCOLOR_TYPE)((b) & (0xFF & ~((1<<(8-LLDCOLOR_BITS_B))-1)))) << (LLDCOLOR_BITS_B+LLDCOLOR_SHIFT_B-8))
	#else // LLDCOLOR_BITS_B + LLDCOLOR_SHIFT_B < 8
		#define LLDBLUE_OF(c)			(((c) & (((1<<LLDCOLOR_BITS_B)-1) << LLDCOLOR_SHIFT_B)) << (8-(LLDCOLOR_BITS_B+LLDCOLOR_SHIFT_B)))
		#define LLDRGB2COLOR_B(b)		(((COLOR_TYPE)((b) & (0xFF & ~((1<<(8-LLDCOLOR_BITS_B))-1)))) >> (8-(LLDCOLOR_BITS_B+LLDCOLOR_SHIFT_B)))
	#endif
	#define LLDLUMA_OF(c)				((LLDRED_OF(c)+((uint16_t)LLDGREEN_OF(c)<<1)+LLDBLUE_OF(c))>>2)
	#define LLDEXACT_RED_OF(c)			(((uint16_t)(((c)>>LLDCOLOR_SHIFT_R)&((1<<LLDCOLOR_BITS_R)-1))*255)/((1<<LLDCOLOR_BITS_R)-1))
	#define LLDEXACT_GREEN_OF(c)		(((uint16_t)(((c)>>LLDCOLOR_SHIFT_G)&((1<<LLDCOLOR_BITS_G)-1))*255)/((1<<LLDCOLOR_BITS_G)-1))
	#define LLDEXACT_BLUE_OF(c)			(((uint16_t)(((c)>>LLDCOLOR_SHIFT_B)&((1<<LLDCOLOR_BITS_B)-1))*255)/((1<<LLDCOLOR_BITS_B)-1))
	#define LLDEXACT_LUMA_OF(c)			((LLDEXACT_RED_OF(c)+((uint16_t)LLDEXACT_GREEN_OF(c)<<1)+LLDEXACT_BLUE_OF(c))>>2)
	#define LLDLUMA2COLOR(l)			(LLDRGB2COLOR_R(l) | LLDRGB2COLOR_G(l) | LLDRGB2COLOR_B(l))
	#define LLDRGB2COLOR(r,g,b)			(LLDRGB2COLOR_R(r) | LLDRGB2COLOR_G(g) | LLDRGB2COLOR_B(b))

	// Calculate LLDHTML2COLOR
	#if LLDCOLOR_BITS_R + LLDCOLOR_SHIFT_R == 24
		#define LLDHTML2COLOR_R(h)		((h) & ((0xFF & ~((1<<(8-LLDCOLOR_BITS_R))-1))<<16))
	#elif COLOR_BITS_R + COLOR_SHIFT_R > 24
		#define LLDHTML2COLOR_R(h)		(((h) & ((0xFF & ~((1<<(8-LLDCOLOR_BITS_R))-1))<<16)) << (LLDCOLOR_BITS_R+LLDCOLOR_SHIFT_R-24))
	#else // COLOR_BITS_R + COLOR_SHIFT_R < 24
		#define LLDHTML2COLOR_R(h)		(((h) & ((0xFF & ~((1<<(8-LLDCOLOR_BITS_R))-1))<<16)) >> (24-(LLDCOLOR_BITS_R+LLDCOLOR_SHIFT_R)))
	#endif
	#if LLDCOLOR_BITS_G + LLDCOLOR_SHIFT_G == 16
		#define LLDHTML2COLOR_G(h)		((h) & ((0xFF & ~((1<<(8-LLDCOLOR_BITS_G))-1))<<8))
	#elif LLDCOLOR_BITS_G + LLDCOLOR_SHIFT_G > 16
		#define LLDHTML2COLOR_G(h)		(((h) & ((0xFF & ~((1<<(8-LLDCOLOR_BITS_G))-1))<<8)) << (LLDCOLOR_BITS_G+LLDCOLOR_SHIFT_G-16))
	#else // LLDCOLOR_BITS_G + LLDCOLOR_SHIFT_G < 16
		#define LLDHTML2COLOR_G(h)		(((h) & ((0xFF & ~((1<<(8-LLDCOLOR_BITS_G))-1))<<8)) >> (16-(LLDCOLOR_BITS_G+LLDCOLOR_SHIFT_G)))
	#endif
	#if LLDCOLOR_BITS_B + LLDCOLOR_SHIFT_B == 8
		#define LLDHTML2COLOR_B(h)		((h) & (0xFF & ~((1<<(8-LLDCOLOR_BITS_B))-1)))
	#elif LLDCOLOR_BITS_B + LLDCOLOR_SHIFT_B > 8
		#define LLDHTML2COLOR_B(h)		(((h) & (0xFF & ~((1<<(8-LLDCOLOR_BITS_B))-1))) << (LLDCOLOR_BITS_B+LLDCOLOR_SHIFT_B-8))
	#else // LLDCOLOR_BITS_B + LLDCOLOR_SHIFT_B < 8
		#define LLDHTML2COLOR_B(h)		(((h) & (0xFF & ~((1<<(8-LLDCOLOR_BITS_B))-1))) >> (8-(LLDCOLOR_BITS_B+LLDCOLOR_SHIFT_B)))
	#endif
	#define LLDHTML2COLOR(h)		((LLDCOLOR_TYPE)(LLDHTML2COLOR_R(h) | LLDHTML2COLOR_G(h) | LLDHTML2COLOR_B(h)))

//-------------------------
//	Gray-scale color system
//-------------------------
#elif (GDISP_LLD_PIXELFORMAT & GDISP_COLORSYSTEM_MASK) == GDISP_COLORSYSTEM_GRAYSCALE
	#define LLDCOLOR_SYSTEM			GDISP_COLORSYSTEM_GRAYSCALE

	// Calculate the number of bits and shifts
	#define LLDCOLOR_BITS			(GDISP_LLD_PIXELFORMAT & 0xFF)
	#define LLDCOLOR_BITS_R			LLDCOLOR_BITS
	#define LLDCOLOR_BITS_G			LLDCOLOR_BITS
	#define LLDCOLOR_BITS_B			LLDCOLOR_BITS
	#define LLDCOLOR_SHIFT_R		0
	#define LLDCOLOR_SHIFT_G		0
	#define LLDCOLOR_SHIFT_B		0

	// From the number of bits determine COLOR_TYPE, COLOR_TYPE_BITS and masking
	#if LLDCOLOR_BITS <= 8
		#define LLDCOLOR_TYPE		uint8_t
		#define LLDCOLOR_TYPE_BITS	8
	#else
		#error "GDISP: Cannot define gray-scale low level driver color types with more than 8 bits"
	#endif
	#if LLDCOLOR_TYPE_BITS == LLDCOLOR_BITS
		#define LLDCOLOR_NEEDS_MASK	FALSE
	#else
		#define LLDCOLOR_NEEDS_MASK	TRUE
	#endif
	#define LLDCOLOR_MASK()			((1 << LLDCOLOR_BITS)-1)

	#if COLOR_BITS == 1
		#define LLDRGB2COLOR(r,g,b)		(((r)|(g)|(b)) ? 1 : 0)
		#define LLDLUMA2COLOR(l)		((l) ? 1 : 0)
		#define LLDHTML2COLOR(h)		((h) ? 1 : 0)
		#define LLDLUMA_OF(c)			((c) ? 255 : 0)
		#define LLDEXACT_LUMA_OF(c)		LLDLUMA_OF(c)
	#else
		// They eye is more sensitive to green
		#define LLDRGB2COLOR(r,g,b)		((LLDCOLOR_TYPE)(((uint16_t)(r)+(g)+(g)+(b)) >> (10-LLDCOLOR_BITS)))
		#define LLDLUMA2COLOR(l)		((LLDCOLOR_TYPE)((l)>>(8-LLDCOLOR_BITS)))
		#define LLDHTML2COLOR(h)		((LLDCOLOR_TYPE)(((((h)&0xFF0000)>>16)+(((h)&0x00FF00)>>7)+((h)&0x0000FF)) >> (10-LLDCOLOR_BITS)))
		#define LLDLUMA_OF(c)			(((c) & ((1<<LLDCOLOR_BITS)-1)) << (8-LLDCOLOR_BITS))
		#define LLDEXACT_LUMA_OF(c)		((((uint16_t)(c) & ((1<<LLDCOLOR_BITS)-1))*255)/((1<<LLDCOLOR_BITS)-1))
	#endif

	#define LLDRED_OF(c)			LLDLUMA_OF(c)
	#define LLDGREEN_OF(c)			LLDLUMA_OF(c)
	#define LLDBLUE_OF(c)			LLDLUMA_OF(c)
	#define LLDEXACT_RED_OF(c)		LLDEXACT_LUMA_OF(c)
	#define LLDEXACT_GREEN_OF(c)	LLDEXACT_LUMA_OF(c)
	#define LLDEXACT_BLUE_OF(c)		LLDEXACT_LUMA_OF(c)

//-------------------------
//	Palette color system
//-------------------------
#elif (GDISP_LLD_PIXELFORMAT & GDISP_COLORSYSTEM_MASK) == GDISP_COLORSYSTEM_PALETTE
	#define LLDCOLOR_SYSTEM			GDISP_COLORSYSTEM_PALETTE

	#error "GDISP: A palette color system for low level drivers is not currently supported"

//-------------------------
//	Some other color system
//-------------------------
#else
	#error "GDISP: Unsupported color system for low level drivers"
#endif

/* Which is the larger color type */
#if COLOR_BITS > LLDCOLOR_BITS
	#define LARGER_COLOR_BITS	COLOR_BITS
	#define LARGER_COLOR_TYPE	COLOR_TYPE
#else
	#define LARGER_COLOR_BITS	LLDCOLOR_BITS
	#define LARGER_COLOR_TYPE	LLDCOLOR_TYPE
#endif

/**
 * @brief	Controls color conversion accuracy for a low level driver
 * @details	Should higher precision be used when converting colors.
 * @note	Color conversion is only necessary if GDISP_PIXELFORMAT != GDISP_LLD_PIXELFORMAT
 * @note	It only makes sense to turn this on if you have a high bit depth display but
 * 			are running the application in low bit depths.
 * @note	To achieve higher color accuracy bit shifting is replaced with multiplies and divides.
 */
#ifndef GDISP_HARDWARE_USE_EXACT_COLOR
	#if LLDCOLOR_BITS_R - COLOR_BITS_R >= LLDCOLOR_BITS_R/2 || LLDCOLOR_BITS_G - COLOR_BITS_G >= LLDCOLOR_BITS_G/2 || LLDCOLOR_BITS_B - COLOR_BITS_B >= LLDCOLOR_BITS_B/2
		#define GDISP_HARDWARE_USE_EXACT_COLOR	TRUE
	#else
		#define GDISP_HARDWARE_USE_EXACT_COLOR	FALSE
	#endif
#endif

/* Low level driver pixel format conversion functions */
#if GDISP_PIXELFORMAT == GDISP_LLD_PIXELFORMAT || defined(__DOXYGEN__)
	/**
	 * @brief	Convert from a standard color format to the low level driver pixel format
	 * @note	For use only by low level drivers
	 */
	#define gdispColor2Native(c)	(c)
	/**
	 * @brief	Convert from a low level driver pixel format to the standard color format
	 * @note	For use only by low level drivers
	 */
	#define gdispNative2Color(c)	(c)
#else
	LLDCOLOR_TYPE gdispColor2Native(color_t c);
	color_t gdispNative2Color(LLDCOLOR_TYPE c);
#endif

#endif	/* GFX_USE_GDISP */

#endif	/* _GDISP_LLD_H */
/** @} */
